import ssl
from requests import get
from re import compile, sub, search
from json import loads
from subprocess import Popen
from os import mkdir
from os.path import exists
from shutil import move
from concurrent.futures.thread import ThreadPoolExecutor

headers = {
    'accept':
    '*/*',
    'Accept-Language':
    'zh-CN,zh;q=0.9,en;q=0.8',
    'User-Agent':
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.79 Safari/537.36 Edg/88.0.705.41'
}

# 将完成视频下载的视频名从后插入到列表中，前端从前开始从列表中取值来实现前后端通信
schedule = []
# ssl模块，处理https请求失败问题，生成证书上下文
ssl._create_default_https_context = ssl._create_unverified_context


def remove_space(string):
    """
    清除文件名中的违规字符和空格
    """
    pattern = compile(r'[/?:*|"”“<> \\]')
    return sub(pattern, '', string)


def re_video_info(text, pattern):
    '''利用正则表达式匹配出视频信息并转化成json'''
    match = search(pattern, text)
    return loads(match.group(1))


def get_video_information(html, quality):
    '''获取音频、视频的信息'''
    # quality为视频画质索引，0为最高画质，依次递减
    video_info_temp = re_video_info(html,
                                    '__playinfo__=(.*?)</script><script>')
    # 获取视频时长
    # video_duration = video_info_temp['data']['dash']['duration']

    # 获取视频链接
    try:
        # 防止要下载的是音频，没有视频
        video_url = video_info_temp['data']['dash']['video'][quality][
            'baseUrl']
    except TypeError:
        video_url = None

    # 获取音频链接
    try:
        # 有的视频没有音频部分,就离谱
        audio_url = video_info_temp['data']['dash']['audio'][quality][
            'baseUrl']
    except TypeError:
        audio_url = None

    return [video_url, audio_url]


def merge_files(video_name, folder_path):
    '''使用ffmpeg单个视频音频合并'''
    # 合并音频、视频文件
    merge = f'ffmpeg -i "download/{video_name}_video.mp4" -i "download/{video_name}_audio.mp4" -c copy "{folder_path}/{video_name}.mp4" -y -loglevel quiet'
    delete = f'del /F /Q download\{video_name}_audio.mp4 download\{video_name}_video.mp4'
    command = merge + ' & ' + delete

    Popen(command, shell=True)


def download_video(urls, file_name, folder_path):
    """
    下载单个音频、视频，并合并
    """

    global schedule
    all = ((urls[0], 'video'), (urls[1], 'audio'))
    # 记录下载的音频和视频
    files = []
    for i in all:
        # url不为None时
        if i[0]:
            headers['Range'] = 'bytes=0-'
            response = get(i[0], headers=headers)
            with open(f'download/{file_name}_{i[1]}.mp4', 'ab') as f:
                f.write(response.content)
            files.append(f'download/{file_name}_{i[1]}.mp4')
    if len(files) == 2:
        merge_files(file_name, folder_path)
    else:
        move(files[0], f'{folder_path}/{file_name}.mp4')
    schedule.append(f'{file_name}下载完成')


def single_download(html, file_name, folder_path, quality=0):
    """
    下载单个视频
    """
    video_urls = get_video_information(html, quality)

    download_video((video_urls[0], video_urls[1]), file_name, folder_path)


def batch_video(url, folder_path, page, quality):
    global schedule
    html = get(url, headers).text
    # 获取视频名称
    file_name = search(f'"page":{page},"from":"vupload","part":"(.*?)"',
                       html).group(1)
    file_name = remove_space(file_name)
    if exists(f'{folder_path}/{file_name}.mp4'):
        schedule.append(f'{file_name}下载完成')
        return

    single_download(html, file_name, folder_path, quality)


def batch_download(number, url, folder_path, quality=0):
    """
    批量下载系列视频
    """

    # 定义一个线程池
    pool = ThreadPoolExecutor(8)

    for i in range(1, number + 1):
        # 将任务加入线程池
        pool.submit(batch_video, f'{url}?p={i}', folder_path, i, quality)

    pool.shutdown(wait=False)


def check(path):
    """检查程序要用到的文件夹是否都存在"""
    if not exists('download'):
        mkdir('download')
    if not exists(path):
        try:
            mkdir(path)
        except Exception:
            return False
    return True


def get_info(url):
    # 更新请求头
    headers.update({"Referer": url})

    try:
        html = get(url, headers).text
    except Exception:
        return False
    # 获取视频数量
    videos_num = int(
        search(r'window.__INITIAL_STATE__={.*?"videos":(\d+),', html).group(1))
    video_info_temp = re_video_info(html,
                                    '__playinfo__=(.*?)</script><script>')
    # 获取所有视频质量
    video_qualitys = video_info_temp['data']['accept_description']
    return videos_num, video_qualitys


def download(url, path, num, quality):
    """
    下载视频
    """

    global schedule
    html = get(url, headers).text
    folder_name = search(r'<title .*?>(.*?)_哔哩哔哩.*?</title>', html).group(1)
    folder_name = remove_space(folder_name)
    folder_path = f'{path}/{folder_name}'

    if not exists(folder_path):
        mkdir(folder_path)
    if exists(f'{folder_path}/{folder_name}.mp4'):
        schedule.append(f'{folder_name}下载完成')
        return
    if num == 1:
        single_download(html, folder_name, folder_path, quality)
    else:
        batch_download(num, url, folder_path, quality)
